﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace MassSpring
{
    /**
     * A camera class for the XNA framework.
     * Implements a 5-DOF first person shooter camera.
     */

    public class FreeFlightCamera : ICamera
    {
        public float leftrightrot = 0.0f; //Rotation around the up vector
        public float updownrot = 0.0f; //Rotation around the side vector
        private float moveSpeed;
        private float rotationSpeed; // In radians
        MouseState originalMouseState;
        public FreeFlightCamera(Vector3 startPosition, float moveSpeed = 40.0f, float rotationSpeed = 0.3f)
            : base(startPosition)
        {
            this.moveSpeed = moveSpeed;
            this.rotationSpeed = rotationSpeed;
            UpdateViewMatrix();
        }

        public override Matrix MirroredCameraViewMatrix(float planeHeight) //Returns the viewmatrix mirrored on a plane at height planeheight with normal (0,1,0)
        {
            Matrix cameraRotation = Matrix.CreateRotationX(updownrot) *
                Matrix.CreateRotationY(leftrightrot);

            Vector3 reflCameraPosition = cameraPosition;

            reflCameraPosition.Y = -cameraPosition.Y + planeHeight * 2;

            Vector3 cameraOriginalTarget = new Vector3(0, 0, -1); //X axis is right to left, Y axis is up/down, Z axis is forward/backward
            //Rotate camera target vector to match the up/down + left/right rotation
            Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
            //Translate camera target to camera position
            Vector3 cameraFinalTarget = cameraRotatedTarget + cameraPosition;
            Vector3 reflTargetPos = cameraFinalTarget;
            reflTargetPos.Y = -cameraFinalTarget.Y + planeHeight * 2;

            Vector3 cameraRight = Vector3.Transform(new Vector3(1, 0, 0), cameraRotation);
            Vector3 invUpVector = Vector3.Cross(cameraRight, reflTargetPos - reflCameraPosition);

            return Matrix.CreateLookAt(reflCameraPosition, reflTargetPos, invUpVector);
        }

        protected override void UpdateViewMatrix()
        {
            Matrix cameraRotation = Matrix.CreateRotationX(updownrot) *
                Matrix.CreateRotationY(leftrightrot);

            Vector3 cameraOriginalTarget = new Vector3(0, 0, -1); //X axis is right to left, Y axis is up/down, Z axis is forward/backward
            //Rotate camera target vector to match the up/down + left/right rotation
            Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
            //Translate camera target to camera position
            Vector3 cameraFinalTarget = cameraRotatedTarget + cameraPosition;

            Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);
            Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);

            cameraViewMatrix = Matrix.CreateLookAt(cameraPosition, cameraFinalTarget, cameraRotatedUpVector);
        }

        public override void LoadContent()
        {
            Mouse.SetPosition(Game1.Instance.GraphicsDevice.Viewport.Width / 2, Game1.Instance.GraphicsDevice.Viewport.Height / 2);
            originalMouseState = Mouse.GetState();
        }

        public override void ProcessInput(float elapsed)
        {
            //Resolve camera rotation
            MouseState currentMouseState = Mouse.GetState();
            if (currentMouseState != originalMouseState)
            {
                float xDifference = (float)currentMouseState.X - (float)originalMouseState.X;
                float yDifference = (float)currentMouseState.Y - (float)originalMouseState.Y;
                leftrightrot = leftrightrot - rotationSpeed * xDifference * elapsed;
                updownrot = updownrot - rotationSpeed * yDifference * elapsed;
                if (updownrot > MathHelper.PiOver2 - 0.1f)
                    updownrot = (float)MathHelper.PiOver2 - 0.1f;
                if (updownrot < -(MathHelper.PiOver2 - 0.1f))
                    updownrot = (float)-(MathHelper.PiOver2 - 0.1f);

                Mouse.SetPosition(Game1.Instance.GraphicsDevice.Viewport.Width / 2, Game1.Instance.GraphicsDevice.Viewport.Height / 2);
            }
            originalMouseState = Mouse.GetState();

            //Resolve camera movement
            Vector3 moveVector = new Vector3(0, 0, 0);
            KeyboardState keyState = Keyboard.GetState();
            if (keyState.IsKeyDown(Keys.Up) || keyState.IsKeyDown(Keys.W))
                moveVector += new Vector3(0, 0, -1);
            if (keyState.IsKeyDown(Keys.Down) || keyState.IsKeyDown(Keys.S))
                moveVector += new Vector3(0, 0, 1);
            if (keyState.IsKeyDown(Keys.Right) || keyState.IsKeyDown(Keys.D))
                moveVector += new Vector3(1, 0, 0);
            if (keyState.IsKeyDown(Keys.Left) || keyState.IsKeyDown(Keys.A))
                moveVector += new Vector3(-1, 0, 0);
            if (keyState.IsKeyDown(Keys.Q))
                moveVector += new Vector3(0, 1, 0);
            if (keyState.IsKeyDown(Keys.Z))
                moveVector += new Vector3(0, -1, 0);

            Matrix cameraRotation = Matrix.CreateRotationX(updownrot)
                * Matrix.CreateRotationY(leftrightrot);
            Vector3 rotatedMove = Vector3.Transform(moveVector * elapsed, cameraRotation);
            cameraPosition += rotatedMove * moveSpeed;
        }

        public override void FixUp()
        {

        }

    }
}
